/**
 * 
 */
package galacticthrone.screen.map;

import galacticthrone.map.data.obs.Ob;
import galacticthrone.map.render.VisionMap;
import galacticthrone.properties.Paths;
import galacticthrone.screen.text.BitmapFont;

import javax.media.opengl.GL;

import javagame.core.main.Temporal;
import javagame.core.main.Time;
import javagame.impl.io.video.camera.XYCam;

// TODO Javadoc
/**
 * <br>
 *
 * @author Jaco van der Westhuizen
 */
public class MapRenderer extends XYCam implements Temporal
{
	public static final BitmapFont FONT = new BitmapFont(Paths.FONTS + "StarCon Smooth 64.png");

    private static final float PAN_VELOCITY = 40.0f;
    private static float ZOOM_VELOCITY = 4.0f;
	private static final float ZOOM_STEP = 1.414f;
	private static float minZoom = 2;

    protected VisionMap map = null;

    protected boolean init = false;
    protected float destPosX;
    protected float destPosY;
    protected float destZoom;
    protected float maxZoom;

    public MapRenderer(float posX, float posY, float zoom)
    {
        super();
        
        this.setParams(posX, posY, zoom);
    }
    
    /* (non-Javadoc)
     * @see javagame.core.io.Screen.ScreenRenderer#loadAssets(javax.media.opengl.GL, boolean)
     */
    @Override
    public void loadAssets(GL gl, boolean isInitial)
    {
    	if (isInitial)
    	{
        	gl.glEnable(GL.GL_TEXTURE_2D);
    	}

    	// TODO Auto-generated method stub
    }

    /* (non-Javadoc)
     * @see javagame.core.io.Screen.ScreenRenderer#unloadAssets(javax.media.opengl.GL, boolean)
     */
    @Override
    public void unloadAssets(GL gl, boolean isFinal)
    {
    	if (isFinal)
    	{
    		FONT.unload(gl);
    	}
        // TODO Auto-generated method stub
        
    }

    /* (non-Javadoc)
     * @see javagame.core.main.time.Temporal#update(javagame.core.main.time.Time)
     */
    @Override
    public void update(Time time)
    {
        float delta = time.getElapsed().getSeconds();

        if (zoom != destZoom)
        {
            final float change = destZoom / zoom;
            final float max = (float)Math.pow(ZOOM_VELOCITY, delta);
            
            if ((change <= max) && (change >= 1 / max))
                zoom = destZoom;
            else
            {
                if (change > max)
                    zoom *= max;
                else
                    zoom /= max;
            }
        }
        
        if ((posX != destPosX) || (posY != destPosY))
        {
            final float changeX = destPosX - posX;
            final float changeY = destPosY - posY;
            final float d = (float)Math.sqrt(changeX * changeX + changeY * changeY);

            if (d > PAN_VELOCITY * delta)
            {
                final float factor = PAN_VELOCITY * delta / d;
                
                posX += changeX * factor;
                posY += changeY * factor;
            }
            else
            {
                posX = destPosX;
                posY = destPosY;
            }
        }
    }

    /* (non-Javadoc)
     * @see javagame.impl.io.video.camera.XYCam#renderForDisplay(javax.media.opengl.GL)
     */
    @Override
    public void renderForDisplay(GL gl)
    {
        super.renderForDisplay(gl);

        if (map == null)
            return;
        
        map.renderDisplay(gl);
    }

    /* (non-Javadoc)
     * @see javagame.impl.io.video.camera.XYCam#renderForPicking(javax.media.opengl.GL)
     */
    @Override
    public void renderForPicking(GL gl)
    {
        super.renderForPicking(gl);
        
        if (map == null)
            return;

        // Render picking grid
        gl.glPushName(MapScreen.INDEX_MAP_GRID);
        for (int x = (int)Math.floor(minX); x <= maxX; x++)
        {
        	gl.glPushName(x);
            for (int y = (int)Math.floor(minY); y <= maxY; y++)
            {
            	gl.glPushName(y);
            	
            	gl.glBegin(GL.GL_QUADS);
            	gl.glVertex2f(x,   y  );
            	gl.glVertex2f(x,   y+1);
            	gl.glVertex2f(x+1, y+1);
            	gl.glVertex2f(x+1, y  );
            	gl.glEnd();

                gl.glPopName();
            }
            gl.glPopName();
        }
        gl.glPopName();
        
        // Render objects
        gl.glPushName(MapScreen.INDEX_MAP_OBS);
        map.renderPicking(gl, minX, maxX, minY, maxY);
        gl.glPopName();
    }
    
    @Override
    public void setParams(float x, float y, float zoom)
    {
        if (map == null)
        {
            this.posX = x;
            this.posY = y;
            this.zoom = zoom;
            this.init = false;
            return;
        }

        this.destZoom = Math.min(Math.max(zoom, minZoom), maxZoom);
       	this.destPosX = Math.min(Math.max(x, 0.5f), map.getSizeX() + 0.5f);
       	this.destPosY = Math.min(Math.max(y, 0.5f), map.getSizeY() + 0.5f);

        if (!init)
        {
            this.posX = this.destPosX;
            this.posY = this.destPosY;
            this.zoom = this.destZoom;
            this.init = true;
        }
    }

    /**
     * @param map
     */
    public void setMap(VisionMap map)
    {
        this.map = map;
        this.maxZoom = Math.min(map.getSizeX(), map.getSizeY());
        
        if (zoom > maxZoom)
        	destZoom = maxZoom;
    }
    
    void centerOn(int x, int y)
    {
		this.setParams(x + 0.5f, y + 0.5f, this.destZoom);
    }
    
    void zoomStep(int diff)
    {
    	// TODO Zoom to/from the mouse cursor
		this.setParams(this.destPosX, this.destPosY, (float)(this.destZoom * Math.pow(ZOOM_STEP, diff)));
    }
    
    void scroll(float dx, float dy, float dz)
    {
    	this.setParams(destPosX + dx * PAN_VELOCITY, destPosY + dy * PAN_VELOCITY, (float)(destZoom * Math.pow(ZOOM_VELOCITY, dz)));
    }

	/**
     * @return The immediate X position of the camera.
     */
    float getPosX()
    {
	    return posX;
    }

	/**
     * @return The immediate Y position of the camera.
     */
    float getPosY()
    {
	    return posY;
    }

	/**
     * @return The immediate zoom level of the camera.
     */
    float getZoom()
    {
	    return zoom;
    }

	/**
     * @param selection
     */
    void centerOn(Ob ob)
    {
		this.setParams(ob.getPosX(), ob.getPosY(), this.destZoom);
    }
}
